package com.supreme.commonboot.utils;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.reflect.MethodSignature;
import org.aspectj.lang.reflect.SourceLocation;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Objects;
import java.util.function.Consumer;

@Slf4j
public class AspectUtils {

    public static void printJoinPointInfo(JoinPoint joinPoint) {
        //获取目标方法参数信息
        Object[] joinPointArgs = joinPoint.getArgs();
        Arrays.stream(joinPointArgs).forEach(arg -> {
            System.out.printf("joinPointArgs arg:\t%s\n", arg);
        });

        //aop代理对象
        Object joinPointThis = joinPoint.getThis();
        System.out.printf("joinPointThis:\t%s\n", joinPointThis.toString());

        //被代理对象
        Object joinPointTarget = joinPoint.getTarget();
        System.out.printf("joinPointTarget:\t%s\n", joinPointTarget.toString());

        //连接点类型
        String joinPointKind = joinPoint.getKind();
        System.out.printf("joinPointKind:\t%s\n", joinPointKind);

        //返回连接点方法所在类文件中的位置  打印报异常
        SourceLocation joinPointSourceLocation = joinPoint.getSourceLocation();
        System.out.printf("joinPointSourceLocation:\t%s\n",joinPointSourceLocation.toString());

        ///返回连接点静态部分
        JoinPoint.StaticPart joinPointStaticPart = joinPoint.getStaticPart();
        System.out.printf("joinPointStaticPart:\t%s\n",joinPointStaticPart.toString());

        //获取连接点的方法签名对象
        Signature joinPointSignature = joinPoint.getSignature();
        System.out.println("\njoinPointSignature");
        System.out.printf("toString:\t%s\n", joinPointSignature);
        System.out.printf("toLongString:\t%s\n", joinPointSignature.toLongString());
        System.out.printf("toShortString:\t%s\n", joinPointSignature.toShortString());
        System.out.printf("getName:\t%s\n", joinPointSignature.getName());
        System.out.printf("getDeclaringTypeName:\t%s\n", joinPointSignature.getDeclaringTypeName());
        System.out.printf("getDeclaringType getSimpleName:\t%s\n", joinPointSignature.getDeclaringType().getSimpleName());
        System.out.printf("getDeclaringType getTypeName:\t%s\n", joinPointSignature.getDeclaringType().getTypeName());
        System.out.printf("getDeclaringType getName:\t%s\n", joinPointSignature.getDeclaringType().getName());
    }

    public static void printServletRequestInfo() {
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
        if (servletRequestAttributes != null) {
            System.out.println("\nservletRequest");
            HttpServletRequest servletRequest = servletRequestAttributes.getRequest();
            String requestURI = servletRequest.getRequestURI();
            System.out.printf("requestURI:\t%s\n", requestURI);
            StringBuffer requestURL = servletRequest.getRequestURL();
            System.out.printf("requestURI:\t%s\n", requestURL);
            String requestMethod = servletRequest.getMethod();
            System.out.printf("requestMethod:\t%s\n", requestMethod);
        }
    }

    /**
     * 获取AOP连接点的类或者方法的注解
     * @param joinPoint 连接点
     * @param annotationType 注解类型
     * @param <TR>
     * @return
     */
    public static <TR extends Annotation> TR findTypeOrMethodAnnotation(JoinPoint joinPoint, Class<TR> annotationType) {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();
        TR annotationTypeResult = AnnotationUtils.findAnnotation(method, annotationType);
        if (Objects.nonNull(annotationTypeResult)) {
            return annotationTypeResult;
        } else {
            Class<?> declaringType = methodSignature.getDeclaringType();
            annotationTypeResult = AnnotationUtils.findAnnotation(declaringType, annotationType);
            if (Objects.nonNull(annotationTypeResult)) {
                return annotationTypeResult;
            }
        }

        return null;
    }

    /**
     * 获取AOP连接点的类或者方法的注解
     * @param joinPoint 连接点
     * @param annotationType 注解类型
     * @param consumer 消费者
     * @param <TR>
     * @return
     */
    public static <TR extends Annotation> TR findTypeOrMethodAnnotation(JoinPoint joinPoint, Class<TR> annotationType, Consumer<TR> consumer) {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();
        TR annotationTypeResult = AnnotationUtils.findAnnotation(method, annotationType);
        if (Objects.nonNull(annotationTypeResult)) {
            consumer.accept(annotationTypeResult);
            return annotationTypeResult;
        } else {
            Class<?> declaringType = methodSignature.getDeclaringType();
            annotationTypeResult = AnnotationUtils.findAnnotation(declaringType, annotationType);
            if (Objects.nonNull(annotationTypeResult)) {
                consumer.accept(annotationTypeResult);
                return annotationTypeResult;
            }
        }

        return null;
    }
}
